﻿using System;
using System.Collections.Generic;
using ImageTagToolViewModel.Interfaces;

namespace ImageTagToolViewModel.Others
{
    /// <summary>
    /// A generic class, that counts the usage of its contents.
    /// </summary>
    public class ListWithReferenceCounter<T>
    {
        private List<T> _Entries;
        private List<int> _References;

        /// <summary>
        /// Gets a copy of the entries as an array.
        /// </summary>
        public T[] Entries
        {
            get { return _Entries.ToArray(); }
        }

        /// <summary>
        /// Gets the number of elements contained in this list.
        /// </summary>
        public int Count
        {
            get { return _Entries.Count; }
        }

        public T this[int index]
        {
            get { return _Entries[index]; }
        }

        /// <summary>
        /// Creates a new instance of the ListWithReferenceCounter generic class.
        /// </summary>
        public ListWithReferenceCounter()
        {
            _Entries = new List<T>();
            _References = new List<int>();
        }

        /// <summary>
        /// Adds an entry to the list. If it is already added, then its reference-counter is increased.
        /// </summary>
        /// <param name="entry">The entry to add.</param>
        public void Add(T entry)
        {
            if (_Entries.Contains(entry))
            {
                int index = _Entries.IndexOf(entry);
                _References[index]++;
            }
            else
            {
                _Entries.Add(entry);
                _References.Add(1);
            }
        }

        /// <summary>
        /// Determines, whether the given entry is present in this list.
        /// </summary>
        /// <param name="entry">The entry to find.</param>
        public bool Contains(T entry)
        {
            return _Entries.Contains(entry);
        }

        /// <summary>
        /// Gets number of the given entry's references.
        /// </summary>
        public int GetReferencesOf(T entry)
        {
            int index = _Entries.IndexOf(entry);
            return _References[index];
        }

        /// <summary>
        /// An entry can only be removed, if it is not used anywhere (has zero references).
        /// </summary>
        /// <param name="entry">The entry to remove.</param>
        public void Remove(T entry)
        {
            int index = _Entries.IndexOf(entry);
            if (index > -1 && _References[index] == 0)
            {
                // If it has such entry and its referce-counter is 0, then:
                _Entries.RemoveAt(index);
                _References.RemoveAt(index);
            }
        }

        /// <summary>
        /// Decreases the reference-counter of the given entry by one.
        /// </summary>
        public void DecreaseReferencesOf(T entry)
        {
            int index = _Entries.IndexOf(entry);
            if (index > -1 && _References[index] > 0)
                _References[index]--;
        }

        /// <summary>
        /// Gets the zero-based position of the entry in the list.
        /// </summary>
        public int IndexOf(T entry)
        {
            return _Entries.IndexOf(entry);
        }

        /// <summary>
        /// Performs a search for such elements, that match the given conditions.
        /// </summary>
        public List<T> FindAll(Predicate<T> match)
        {
            return _Entries.FindAll(match);
        }
    }
}
